package cn.south.campus.business.service.impl;

import cn.south.campus.business.mapper.WalletMapper;
import cn.south.campus.business.model.bo.LoginContext;
import cn.south.campus.business.model.bo.LoginInfo;
import cn.south.campus.business.model.entity.Wallet;
import cn.south.campus.business.model.entity.WithdrawalAudit;
import cn.south.campus.business.model.enums.CampusErrorEnums;
import cn.south.campus.business.model.enums.WalletEnums;
import cn.south.campus.business.model.req.WalletReq;
import cn.south.campus.business.model.vo.WalletVo;
import cn.south.campus.business.model.vo.WithdrawalAuditVo;
import cn.south.campus.business.service.WalletLogService;
import cn.south.campus.business.service.WalletService;
import cn.south.campus.business.service.WithdrawalAuditService;
import cn.south.campus.common.exceptions.BusinessException;
import cn.south.campus.common.model.enums.CommonEnums;
import cn.south.campus.common.utils.AssertUtils;
import cn.south.campus.common.utils.SnowflakeIdWorker;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * <p>
 * 钱包表 服务实现类
 * </p>
 *
 * @author chow
 * @since 2024-11-14
 */
@Service
@AllArgsConstructor
public class WalletServiceImpl extends ServiceImpl<WalletMapper, Wallet> implements WalletService {

    private final WalletMapper walletMapper;
    private final WalletLogService walletLogService;
    private final WithdrawalAuditService withdrawalAuditService;

    @Override
    public void recharge(WalletReq walletReq) {
        Long userId = LoginContext.getContext().getId();
        AssertUtils.isGtE(new BigDecimal(walletReq.getAmount()), BigDecimal.ZERO, CampusErrorEnums.MONEY_IS_ILLEGAL);
        Wallet wallet = walletMapper.selectOne(new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, userId));
        //@TODO 接入支付
        BigDecimal amount = new BigDecimal(walletReq.getAmount());
        wallet.setBalance(wallet.getBalance().add(amount));
        walletMapper.updateById(wallet);
        walletLogService.insertLog(amount, userId
                , SnowflakeIdWorker.getId().toString()
                , WalletEnums.WalletTypeEnums.RECHARGE
                , WalletEnums.WalletLogStatusEnums.RECEIVED);
    }

    @Override
    public void initWallet(Long id) {
        Wallet wallet = new Wallet();
        wallet.setUserId(id);
        wallet.setBalance(BigDecimal.ZERO);
        wallet.setFrozenAmount(BigDecimal.ZERO);
        walletMapper.insert(wallet);
    }

    @Override
    public void subtractBalance(BigDecimal amount, Long userId) {
        Wallet wallet = getWallet(userId);
        if (wallet.getBalance().compareTo(amount) < 0) {
            throw new BusinessException(CampusErrorEnums.WALLET_BALANCE_NOT_ENOUGH);
        }
        wallet.setBalance(wallet.getBalance().subtract(amount));
        walletMapper.updateById(wallet);
    }

    @Override
    public void addBalance(BigDecimal amount, Long userId) {
        Wallet wallet = getWallet(userId);
        wallet.setBalance(wallet.getBalance().add(amount));
        walletMapper.updateById(wallet);
    }

    @Override
    public void subFreezeAddBalance(BigDecimal amount, Long userId) {
        Wallet wallet = getWallet(userId);
        if (wallet.getFrozenAmount().compareTo(amount) < 0) {
            throw new BusinessException(CommonEnums.SYSTEM_ERROR);
        }
        wallet.setFrozenAmount(wallet.getFrozenAmount().subtract(amount));
        wallet.setBalance(wallet.getBalance().add(amount));
        walletMapper.updateById(wallet);
    }

    @Override
    public void addFreezeBalance(BigDecimal amount, Long userId) {
        Wallet wallet = getWallet(userId);
        wallet.setFrozenAmount(wallet.getFrozenAmount().add(amount));
        walletMapper.updateById(wallet);
    }

    @Override
    public void subBalanceAddFreeze(BigDecimal amount, Long userId) {
        Wallet wallet = getWallet(userId);
        if (wallet.getBalance().compareTo(amount) < 0) {
            throw new BusinessException(CampusErrorEnums.WALLET_BALANCE_NOT_ENOUGH);
        }
        wallet.setFrozenAmount(wallet.getFrozenAmount().add(amount));
        wallet.setBalance(wallet.getBalance().subtract(amount));
        walletMapper.updateById(wallet);
    }

    @Override
    public void subFreezeBalance(BigDecimal amount, Long userId) {
        Wallet wallet = getWallet(userId);
        if (wallet.getFrozenAmount().compareTo(amount) < 0) {
            throw new BusinessException(CommonEnums.SYSTEM_ERROR);
        }
        wallet.setFrozenAmount(wallet.getFrozenAmount().subtract(amount));
        walletMapper.updateById(wallet);
    }

    private Wallet getWallet(Long userId) {
        return walletMapper.selectOne(new LambdaQueryWrapper<Wallet>()
                .eq(userId == null, Wallet::getUserId, LoginContext.getContext().getId())
                .eq(userId != null, Wallet::getUserId, userId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdraw(WalletReq walletReq) {
        LoginInfo context = LoginContext.getContext();
        BigDecimal amount = new BigDecimal(walletReq.getAmount());
        AssertUtils.isGtE(amount, BigDecimal.ZERO, CampusErrorEnums.MONEY_IS_ILLEGAL);
        Wallet wallet = walletMapper.selectOne(new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, context.getId()));
        if (wallet.getBalance().compareTo(amount) < 0) {
            throw new BusinessException(CampusErrorEnums.WALLET_BALANCE_NOT_ENOUGH);
        }
        //@TODO 接入支付
        wallet.setBalance(wallet.getBalance().subtract(amount));
        wallet.setFrozenAmount(wallet.getFrozenAmount().add(amount));
        walletMapper.updateById(wallet);
        String orderNo = SnowflakeIdWorker.getId().toString();
        withdrawalAuditService.insertAudit(amount, orderNo);
        walletLogService.insertLog(amount, context.getId(), orderNo
                , WalletEnums.WalletTypeEnums.WITHDRAW
                , WalletEnums.WalletLogStatusEnums.PENDING);
    }

    @Override
    public WalletVo queryBalance() {
        LoginInfo context = LoginContext.getContext();
        Wallet wallet = walletMapper.selectOne(new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, context.getId()));
        return WalletVo.convert(wallet);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdrawCallback(WithdrawalAuditVo withdrawalAuditVo) {
        WithdrawalAudit withdrawalAudit = withdrawalAuditService.getOne(new LambdaQueryWrapper<WithdrawalAudit>()
                .eq(WithdrawalAudit::getId, withdrawalAuditVo.getId()));
        if (!withdrawalAudit.getStatus().equals(WalletEnums.WithdrawalAuditEnums.PENDING.getCode())) {
            return;
        }
        Wallet wallet = walletMapper.selectOne(new LambdaQueryWrapper<Wallet>().eq(Wallet::getUserId, withdrawalAudit.getUserId()));
        AssertUtils.isNotNull(wallet, CommonEnums.DATA_HAS_NOT_ERROR);
        BigDecimal amount = withdrawalAudit.getAmount();
        if (withdrawalAuditVo.getStatus().equals(WalletEnums.WithdrawalAuditEnums.REJECT.getCode())) {
            //@todo 接入支付,提现
            wallet.setBalance(wallet.getBalance().add(amount));
            walletMapper.updateById(wallet);
            walletLogService.deleteLog(withdrawalAuditVo.getUserId(), withdrawalAuditVo.getBizNo());
            return;
        }
        wallet.setFrozenAmount(wallet.getFrozenAmount().subtract(amount));
        walletMapper.updateById(wallet);
        walletLogService.updateLog(withdrawalAuditVo.getUserId(), withdrawalAuditVo.getBizNo(), WalletEnums.WalletLogStatusEnums.WITHDRAWED);
    }


}
